package org.timemachine.query;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.timemachine.query.util.QueryConstants;
import org.timemachine.query.util.QueryParserInterface;
import org.timemachine.query.util.QueryParserToolInterface;

public class TimeMachineQueryParser implements QueryConstants,
		QueryParserInterface, QueryParserToolInterface {

	@Override
	public String getJoinStatement(String baseSql) {

		String statement = discardFromClauseCommands(baseSql);

		if (statement != null && !"".equals(statement)
				&& statement.toUpperCase().contains(JOIN)) {

			for (String _command : new ArrayList<String>(
					Arrays.asList(joinClauseCommands))) {

				if (statement.toUpperCase().contains(_command)) {
					statement = statement.substring(statement.toUpperCase()
							.indexOf(_command) + _command.length(),
							statement.length());
				}
			}

			if (statement.toUpperCase().contains(JOIN)) {
				statement = statement.substring(statement.toUpperCase()
						.indexOf(JOIN) + JOIN.length(), statement.length());
			}

			return statement;
		} else {
			return "";
		}

	}

	@Override
	public String discardJoinClauseCommands(String baseSql) {

		String statement = discardFromClauseCommands(baseSql);

		if (statement != null && !"".equals(statement)) {

			for (String _command : new ArrayList<String>(
					Arrays.asList(joinClauseCommands))) {

				if (statement.toUpperCase().contains(_command)) {
					statement = statement.substring(0, statement.toUpperCase()
							.indexOf(_command));
				}
			}

			if (statement.toUpperCase().contains(JOIN)) {
				statement = statement.substring(0, statement.toUpperCase()
						.indexOf(JOIN));
			}

			return statement;
		} else {
			return statement;
		}

	}

	@Override
	public QueryEntity parseJoinStatement(
			QueryEntity queryEntity, String joinStatement) {

		String resStatement = joinStatement;

		if (resStatement.toUpperCase().contains(AS_ALLIAS)) {
			resStatement = resStatement.replace(AS_ALLIAS, "");
			resStatement = resStatement.replace(AS_ALLIAS_LOWER, "");
		}

		resStatement = resStatement.trim();

		String[] res = resStatement.substring(
				resStatement.indexOf(queryEntity.getEntityAlias() + "."),
				resStatement.length()).split(" ");

		QueryEntity joinQueryEntity = new QueryEntity();
		String entityName = Arrays.asList(res).get(0)
				.substring(Arrays.asList(res).get(0).indexOf(".") + 1);
		joinQueryEntity.setEntityFieldName(entityName);

		// upper first letter for Entity
		entityName = entityName.substring(0, 1).toUpperCase()
				+ entityName.substring(1);
		joinQueryEntity.setEntityName(entityName);

		joinQueryEntity.setEntityAlias(Arrays.asList(res).get(1).trim());

		queryEntity.getJoinList().add(joinQueryEntity);
		return queryEntity;
	}

	@Override
	public String getSelectFromStatement(String sql) {

		if (sql != null && sql.toUpperCase().contains(FROM_CLAUSE)) {
			return (sql.substring(0, sql.toUpperCase().indexOf(FROM_CLAUSE))
					.trim()) + " from ";
		} else {
			return "";
		}
	}

	@Override
	public String getQueryAfterFrom(String sql) {

		if (sql != null && sql.toUpperCase().contains(FROM_CLAUSE)) {
			return sql.substring(
					sql.toUpperCase().indexOf(FROM_CLAUSE)
							+ FROM_CLAUSE.length(), sql.length()).trim();
		} else {
			return "";
		}

	}

	@Override
	public String getWhereStatment(String sql) {

		String statement = getQueryAfterFrom(sql);

		if (statement != null && statement.toUpperCase().contains(WHERE_CLAUSE)) {
			return statement.substring(
					statement.toUpperCase().indexOf(WHERE_CLAUSE)
							+ WHERE_CLAUSE.length(), statement.length()).trim();
		} else {
			return "";
		}
	}

	@Override
	public String getQueryBetweenFromAndWhere(String sql) {

		String statement = getQueryAfterFrom(sql);

		if (statement != null && statement.toUpperCase().contains(WHERE_CLAUSE)) {
			return statement.substring(0,
					statement.toUpperCase().indexOf(WHERE_CLAUSE));
		} else {
			return statement;
		}
	}

	@Override
	public String discardFromClauseCommands(String baseSql) {

		String statement = getQueryBetweenFromAndWhere(baseSql);

		if (statement != null && !"".equals(statement)) {

			for (String _command : new ArrayList<String>(
					Arrays.asList(fromClauseCommands))) {

				if (statement.toUpperCase().contains(_command)) {
					statement = statement.substring(0, statement.toUpperCase()
							.indexOf(_command));
				}
			}

			return statement;
		} else {
			return statement;
		}
	}

	@Override
	public Map<String, String> getTableNamesAndAlias(String baseSql) {

		String statement = discardJoinClauseCommands(baseSql);

		Map<String, String> resultMap = new HashMap<String, String>();
		String res = statement;

		if (statement.toUpperCase().contains(AS_ALLIAS)) {
			res = statement.replace(AS_ALLIAS, "");
			res = statement.replace(AS_ALLIAS_LOWER, "");
		}

		String[] tableAndAliasPair = (res.trim()).split(" +");

		if (tableAndAliasPair != null && tableAndAliasPair.length > 1) {
			resultMap.put(tableAndAliasPair[0].trim(),
					tableAndAliasPair[1].trim());
		}
		return resultMap;
	}

	@Override
	public Map<String, String> prepareTableNames(
			Map<String, String> tableAliasPairs) {

		if (tableAliasPairs != null) {
			Map<String, String> tableAliasParesWithChange = new HashMap<String, String>();
			Iterator it = tableAliasPairs.entrySet().iterator();

			while (it.hasNext()) {
				Map.Entry mapEntry = (Map.Entry) it.next();
				tableAliasParesWithChange.put(mapEntry.getKey() + "Change",
						(String) mapEntry.getValue());
				continue;
			}
			return tableAliasParesWithChange;
		} else {
			return null;
		}

	}

	@Override
	public ArrayList<String> prepareFromStatementForJoin(
			Map<String, String> joinTableAliasPairWithChange,
			String fromObjectAlias, String joinStatementType) {

		ArrayList<String> resultList = new ArrayList<String>();

		StringBuffer buffer = new StringBuffer();
		StringBuffer buffer2 = new StringBuffer();

		if (joinTableAliasPairWithChange != null) {

			Iterator it = joinTableAliasPairWithChange.entrySet().iterator();
			while (it.hasNext()) {

				Map.Entry mapEntry = (Map.Entry) it.next();
				buffer.append(" " + joinStatementType);
				buffer.append(fromObjectAlias);
				buffer.append(".");
				buffer.append(mapEntry.getKey());
				buffer.append(" ");
				buffer.append(mapEntry.getValue());
				buffer.append(" ");
				buffer.append("join");
				buffer.append(" ");
				buffer.append(mapEntry.getValue());
				buffer.append(".beginChangeInfo ");
				buffer.append("cbegin2");
				buffer.append(" ");
				buffer.append("join");
				buffer.append(" ");
				buffer.append(mapEntry.getValue());
				buffer.append(".endChangeInfo ");
				buffer.append("cend2");

				buffer2.append(" and ");
				buffer2.append("cbegin2");
				buffer2.append(".changeTimeStamp < ");
				buffer2.append(":cdate");
				buffer2.append(" and ");
				buffer2.append("cend2");
				buffer2.append(".changeTimeStamp > ");
				buffer2.append(":cdate");

			}

			resultList.add(buffer.toString());
			resultList.add(buffer2.toString());

			return resultList;

		} else {
			return null;
		}

	}

	@Override
	public QueryEntity prepareNewQueryStatementForJoin(
			QueryEntity queryEntity, String joinStatementType) {

		StringBuffer buffer = new StringBuffer();

		for (QueryEntity joinQueryEntity : queryEntity.getJoinList()) {
			String key = joinQueryEntity.getEntityName();
			String value = joinQueryEntity.getEntityAlias();

			buffer.append("select " + value);
			buffer.append(" from " + key);
			buffer.append(" " + value);
			buffer.append(" where " + value);
			buffer.append(".id = :id");

			joinQueryEntity.setSql(buffer.toString());
		}

		return queryEntity;
	}

	@Override
	public ArrayList<String> prepareFromStatement(
			Map<String, String> tableAliasPairWithChange) {

		if (tableAliasPairWithChange != null) {

			ArrayList<String> resultList = new ArrayList<String>();

			StringBuffer buffer = new StringBuffer();
			StringBuffer buffer2 = new StringBuffer();

			Iterator it = tableAliasPairWithChange.entrySet().iterator();
			while (it.hasNext()) {

				Map.Entry mapEntry = (Map.Entry) it.next();

				buffer.append(mapEntry.getKey());
				buffer.append(" ");
				buffer.append(mapEntry.getValue());
				buffer.append(" ");
				buffer.append("join");
				buffer.append(" ");
				buffer.append(mapEntry.getValue());
				buffer.append(".beginChangeInfo ");
				buffer.append("cbegin");
				buffer.append(" ");
				buffer.append("join");
				buffer.append(" ");
				buffer.append(mapEntry.getValue());
				buffer.append(".endChangeInfo ");
				buffer.append("cend");
				buffer.append(" ");

				buffer2.append("cbegin");
				buffer2.append(".changeTimeStamp < ");
				buffer2.append(":cdate");
				buffer2.append(" and ");
				buffer2.append("cend");
				buffer2.append(".changeTimeStamp > ");
				buffer2.append(":cdate");
				buffer2.append(" and ");
				buffer2.append(mapEntry.getValue());
				buffer2.append(".changeType <> 3 ");

			}

			resultList.add(buffer.toString());
			resultList.add(buffer2.toString());

			return resultList;

		} else {
			return null;
		}

	}

	@Override
	public String getJoinStatementType(String baseSql) {

		String statement = getQueryBetweenFromAndWhere(baseSql);

		if (statement != null && !"".equals(statement)) {

			for (String _command : new ArrayList<String>(
					Arrays.asList(joinClauseCommands))) {

				if (statement.toUpperCase().contains(_command)) {
					return _command;
				}
			}

			if (statement.toUpperCase().contains(JOIN)) {
				return JOIN;
			} else {
				return "";
			}
		} else {
			return "";
		}

	}

	@Override
	public String getAlias(String baseSql) {

		Map<String, String> tableAliasPairs = getTableNamesAndAlias(baseSql);

		for (Map.Entry<String, String> entry : tableAliasPairs.entrySet()) {
			return entry.getValue();
		}

		return null;
	}

}
